#!/usr/bin/env python2.5 
# $Id: sshshell.py 9227e81e919a 2012/08/27 15:49:57 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__  = "$Revision: 9227e81e919a $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2012-08-27 15:49:57Z $"

# essential imports
import os
import select
import socket
import sys
import tempfile
import termios
import time
import tty

# My exception class
class TerminalError(Exception):
   pass

# My terminam class
class Terminal(object):
   def __init__(self, Channel):
      self._err = TerminalError()
      self._temp = None
      self._tfh = None
      self._fh = None

      (fileno, self._temp) = tempfile.mkstemp()

      # What is my channel?
      self._channel = Channel
      if not self._channel:
         self._error("invalid channel!")
      else:
         # Set the channel timeout to be infinite
         self._channel.settimeout(0.0)

   def _error(self, Message):
      self.Close()
      if Message:
         # Close the class and raise the error
         raise TerminalError, Message

   def Close(self):
      if self._channel:
         self._channel = None

      if self._tfh:
         self._tfh.close()
         self._tfh = None

      if self._temp:
         try:
            os.unlink(self._temp)
         except OSError, message:
            pass
         finally:
            self._temp = None

      if self._fh:
         self._fh.close()
         self._fh = None

   def GetShell(self):
      # Get the attributes of the stdin
      oldtty = termios.tcgetattr(sys.stdin)
      tty.setraw(sys.stdin.fileno())
      tty.setcbreak(sys.stdin.fileno())

      flag = True
      # simple select and then multiplex between channel and stdin, dumps the
      # output data onto stdout
      while flag:
         read, write, ex = select.select([self._channel, sys.stdin], [], [])
         if self._channel in read:
            try:
               ix = self._channel.recv(1024)
               if not ix:
                  flag = False
               else:
                  sys.stdout.write(ix)
                  sys.stdout.flush()
            except socket.timeout:
               pass

         if sys.stdin in read:
            ix = sys.stdin.read(1)
            if not ix:
               flag = False
            else:
               self._channel.send(ix)

      termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

   def SendFile(self, File, Stdout = True):
      if not Stdout:
         if os.path.exists(self._temp):
            try:
               self._tfh = open(self._temp, "w")
            except IOError, message:
               self._error(message)

      if File:
         if os.path.exists(File):
            try:
               self._fh = open(File, "r")
            except IOError, message:
               self._error(message)

            flag = True
            while flag:
               read, write, ex = select.select(
                     [self._channel, self._fh], [], [])
               if self._channel in read:
                  try:
                     data = self._channel.recv(1024)
                     if not data:
                        flag = False
                     else:
                        if self._tfh:
                           self._tfh.write(data)
                        else:
                           sys.stdout.write(data)
                           sys.stdout.flush()
                  except socket.timeout:
                     pass

               if self._fh in read:
                  data = self._fh.read(1024)
                  if data:
                     for ix in data:
                        self._channel.send(ix)
